#include "vanila/baseobject.h"
#include "vanila/allocator.h"
#include "utils/singleton.h"
#include <cstring>
#include <iostream>

namespace vanila
{
static Allocator* allocator = SINGLETON(Allocator);

ObjectString* ObjectClass::initMethodName = nullptr;
ObjectString* ObjectClass::strMethodName = nullptr;
ObjectString* ObjectClass::hashMethodName = nullptr;
ObjectString* ObjectClass::equalMethodName = nullptr;
ObjectString* ObjectClass::lessMethodName = nullptr;
ObjectString* ObjectClass::greaterMethodName = nullptr;
ObjectString* ObjectClass::addMethodName = nullptr;
ObjectString* ObjectClass::subMethodName = nullptr;
ObjectString* ObjectClass::mulMethodName = nullptr;
ObjectString* ObjectClass::divMethodName = nullptr;

ObjectClass* ObjectClass::list = nullptr;
ObjectClass* ObjectClass::dict = nullptr;
ObjectClass* ObjectClass::set = nullptr;

//! \brief Construct a new Object Class:: Object Class object
ObjectClass::ObjectClass(ObjectString* name, bool isNative) noexcept:
    Object{ObjectType::CLASS}, 
    _isNative{isNative},
    _name{name}, 
    _methods{}
{   
}

//! \brief print object class
//! \param[in] callStr wheather call '__str__' method to print
void ObjectClass::print(bool callStr) const
{
    static_cast<void>(callStr);
    std::cout << "<class '" << this->_name->str() << "'>";
}

//! \brief add a nemed method(native function)
//! \param[in] name method name object
//! \param[in] native native function pointer
void ObjectClass::addMethod(ObjectString* name, NativeFunction native)
{
    ObjectNative* nativeObject = allocator->allocateNative(native);
    this->addMethod(name, Value(nativeObject));
}

//! \brief add a nemed method(native function)
//! \param[in] name method name
//! \param[in] native native function pointer
void ObjectClass::addMethod(const char* name, NativeFunction native)
{
    ObjectString* stringObject = allocator->allocateString(name, strlen(name));
    this->addMethod(stringObject, native);
}

//! \brief cehck wheather name in class
bool ObjectClass::findFiled(ObjectString* name)
{
    return this->_fields.find(name) != this->_fields.end();
}

//! \brief find the filed in specify name
//! \param[in] name filed name
//! \param[out] filed the reference
bool ObjectClass::findFiled(ObjectString* name, Value& filed)
{
    auto iter = this->_fields.find(name);
    if (iter != this->_fields.end())
    {
        filed = iter->second;
        return true;
    }
    return false;
}

//! \param[in] name method name
//! \param[out] method value reference
//! \note return false when no found
bool ObjectClass::findMethod(ObjectString* name, Value& method)
{
    auto iter = this->_methods.find(name);
    if (iter != this->_methods.end())
    {
        method = iter->second;
        return true;
    }
    return false;
}

//! \brief add '__init__' method
void ObjectClass::addInitMethod(NativeFunction native)
{
    ObjectNative* nativeObject = allocator->allocateNative(native);
    this->addInitMethod(Value(nativeObject));
}

//! \brief add '__str__' method
void ObjectClass::addStrMethod(NativeFunction native)
{
    ObjectNative* nativeObject = allocator->allocateNative(native);
    this->addStrMethod(Value(nativeObject));
}

}